package com.franklin.validation.tool.common;


import com.franklin.validation.tool.annotations.JaxrsRequestAnnotation;
import com.franklin.validation.tool.annotations.SpringControllerAnnotation;
import com.franklin.validation.tool.annotations.SpringRequestParamAnnotations;
import com.franklin.validation.tool.annotations.franklin.ParamCheckAnnotations;
import com.franklin.validation.tool.annotations.spring.RequestParamAnnotations;
import com.franklin.validation.tool.common.jaxrs.JaxrsAnnotationHelper;
import com.franklin.validation.tool.common.spring.RequestMappingAnnotationHelper;
import com.franklin.validation.tool.method.Parameter;
import com.franklin.validation.tool.method.action.ModuleHelper;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.stream.Collectors;

/**
 * PsiMethod处理类
 */
public class PsiMethodHelper {
    PsiMethod psiMethod;
    Project myProject;
    Module myModule;

    private String pathSeparator= "/";

    public static PsiMethodHelper create(@NotNull PsiMethod psiMethod) {
        return new PsiMethodHelper(psiMethod);
    }

    public PsiMethodHelper withModule(Module module) {
        this.myModule = module;
        return this;
    }

    protected PsiMethodHelper(PsiMethod psiMethod) {
        this.psiMethod = psiMethod;
    }

    @NotNull
    protected Project getProject() {
        myProject =  psiMethod.getProject();
        return myProject;
    }

    /**
     * 构建URL参数 key value
     * @return
     */
    public String buildParamString() {

//        boolean matchedGet = matchGetMethod();
        // 没指定method 标示支持所有method

        StringBuilder param = new StringBuilder("");
        Map<String, Object> baseTypeParamMap = this.getBaseTypeParameterMap();

        if (baseTypeParamMap != null && baseTypeParamMap.size() > 0) {
            baseTypeParamMap.forEach((s, o) -> param.append(s).append("=").append(o).append("&"));
        }

        return param.length() >0 ? param.deleteCharAt(param.length()-1).toString() : "";
    }

    /*获取方法中基础类型（primitive和string、date等以及这些类型数组）*/
    @NotNull
    public Map<String, Object> getBaseTypeParameterMap() {
        List<Parameter> parameterList = getParameterList();

        Map<String,Object> baseTypeParamMap = new LinkedHashMap<>();

        // 拼接参数
        for (Parameter parameter : parameterList) {
//跳过标注 RequestBody 注解的参数
            if (parameter.isRequestBodyFound()) {
                continue;
            }

            // todo 判断类型
            // 8 PsiPrimitiveType
            // 8 boxed types; String,Date:PsiClassReferenceType == field.getType().getPresentableText()
            String shortTypeName = parameter.getShortTypeName();
            Object defaultValue = PsiClassHelper.getJavaBaseTypeDefaultValue(shortTypeName,parameter.getDefaultValue());
            //简单常用类型
            if (defaultValue != null) {
                baseTypeParamMap.put(parameter.getParamName(),(defaultValue));
                continue;
            }

            PsiClassHelper psiClassHelper = PsiClassHelper.create(psiMethod.getContainingClass());
            PsiClass psiClass = psiClassHelper.findOnePsiClassByClassName(parameter.getParamType(), getProject());

            if (psiClass != null) {
                PsiField[] fields = psiClass.getFields();
                for (PsiField field : fields) {
                    Object fieldDefaultValue  = PsiClassHelper.getJavaBaseTypeDefaultValue(field.getType().getPresentableText());
                    if(fieldDefaultValue != null)
                        baseTypeParamMap.put(field.getName(), fieldDefaultValue);
                }
            }

          /*  PsiClass psiClass2 = psiClassHelper.findOnePsiClassByClassName2(parameter.getParamType(), getProject());
            if (psiClass2 != null) {
                PsiField[] fields = psiClass2.getFields();
                for (PsiField field : fields) {
                    Object fieldDefaultValue  = PsiClassHelper.getJavaBaseTypeDefaultValue(field.getType().getPresentableText());
                    if(fieldDefaultValue != null)
                        baseTypeParamMap.put(field.getName(), fieldDefaultValue);
                }
            }*/
        }
        return baseTypeParamMap;
    }

    /* 基础类型默认值 */
    @Nullable
    public Map<String,Object>  getJavaBaseTypeDefaultValue(String paramName, String paramType) {
        Map<String,Object> paramMap = new LinkedHashMap<>();
        Object paramValue = null;
        paramValue = PsiClassHelper.getJavaBaseTypeDefaultValue(paramType);
        if (paramValue != null) {
            paramMap.put(paramType, paramValue);
        }
        return paramMap;
    }

    @NotNull
    public List<Parameter> getParameterList() {
        List<Parameter> parameterList = new ArrayList<>();
        PsiParameterList psiMethodParameterList = this.psiMethod.getParameterList();
        PsiParameter[] parameters = psiMethodParameterList.getParameters();
        for (PsiParameter psiParameter : parameters) {
            String paramType = psiParameter.getType().getCanonicalText();
            // TODO: 2020/6/29 WebFlux ServerHttpRequest
            if (!paramType.equals("javax.servlet.http.HttpServletRequest") && !paramType.equals("javax.servlet.http.HttpServletResponse")) {
                PsiModifierList modifierList = psiParameter.getModifierList();
                boolean requestBodyFound = modifierList.findAnnotation(SpringRequestParamAnnotations.REQUEST_BODY.getQualifiedName()) != null;
                String paramName = psiParameter.getName();
                String requestName = null;
                //@PathVariable
                PsiAnnotation pathVariableAnno = modifierList.findAnnotation(SpringRequestParamAnnotations.PATH_VARIABLE.getQualifiedName());
                if (pathVariableAnno != null) {
                    requestName = this.getAnnotationValue(pathVariableAnno);
                    Parameter parameter = (new Parameter(paramType, requestName != null ? requestName : paramName)).setRequired(true).requestBodyFound(requestBodyFound);
                    parameter.setPathVal(true);
                    parameterList.add(parameter);
                }

                //@RequestParam
                PsiAnnotation requestParamAnno = modifierList.findAnnotation(SpringRequestParamAnnotations.REQUEST_PARAM.getQualifiedName());
                Parameter parameter;
                if (requestParamAnno != null) {
                    requestName = this.getAnnotationValue(requestParamAnno);
                    parameter = (new Parameter(paramType, requestName != null ? requestName : paramName))
                            .setRequired(this.isRequired(requestParamAnno))
                            .requestBodyFound(requestBodyFound);
                    parameter.setDefaultValue(this.getDefaultValue(requestParamAnno));
                    parameterList.add(parameter);
                }

                //@ParamCheck
                PsiAnnotation paramCheckAnn = modifierList.findAnnotation(ParamCheckAnnotations.PARAM_CHECK.getQualifiedName());
                if (Objects.nonNull(paramCheckAnn)){
                    parameter = this.getParamCheckParam(paramCheckAnn);
                    parameterList.add(parameter);
                }

                if (pathVariableAnno == null && requestParamAnno == null && Objects.isNull(paramCheckAnn)) {
                    parameter = (new Parameter(paramType, paramName)).requestBodyFound(requestBodyFound);
                    parameterList.add(parameter);
                }
            }
        }
        return parameterList;
    }

    public boolean isRequired(PsiAnnotation ann){
        PsiAnnotationMemberValue required = ann.findDeclaredAttributeValue("required");
        if (Objects.nonNull(required) && required instanceof PsiLiteralExpression){
            Object value = ((PsiLiteralExpression) required).getValue();
            return "true".equals(value);
        }
        return true;
    }

    public boolean isSystemDefault(PsiAnnotation ann){
        PsiAnnotationMemberValue defaultValue = ann.findDeclaredAttributeValue("defaultValue");
        if (Objects.nonNull(defaultValue) && defaultValue instanceof PsiLiteralExpression){
            Object value = ((PsiLiteralExpression) defaultValue).getValue();
            return "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n".equals(value);
        }
        return true;
    }

    public String getDefaultValue(PsiAnnotation ann){
        PsiAnnotationMemberValue defaultValue = ann.findDeclaredAttributeValue("defaultValue");
        if (Objects.nonNull(defaultValue) && defaultValue instanceof PsiLiteralExpression){
            Object value = ((PsiLiteralExpression) defaultValue).getValue();
            if ("\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n".equals(value)){
                return "";
            }
            return String.valueOf(value);
        }
        return "";
    }

    public String getPattern(PsiAnnotation ann){
        PsiAnnotationMemberValue pattern = ann.findDeclaredAttributeValue("pattern");
        if (Objects.nonNull(pattern) && pattern instanceof PsiLiteralExpression){
            Object value = ((PsiLiteralExpression) pattern).getValue();
            return String.valueOf(value);
        }
        return "";
    }

    public String getAnnotationValue(PsiAnnotation annotation) {
        String paramName = null;
        PsiAnnotationMemberValue attributeValue = annotation.findDeclaredAttributeValue("value");

        if (attributeValue != null && attributeValue instanceof PsiLiteralExpression) {
            paramName = (String) ((PsiLiteralExpression) attributeValue).getValue();
        }
        return paramName;
    }

    /**
     * 构建RequestBody json 参数
     * @param parameter
     * @return
     */
    public String buildRequestBodyJson(Parameter parameter) {
//        JavaFullClassNameIndex.getInstance();

        Project project = psiMethod.getProject();
        final String  className = parameter.getParamType();
        PsiAnnotation notNullForMapAnn = psiMethod.getModifierList()
                .findAnnotation(ParamCheckAnnotations.NOT_NULL_FOR_MAP.getQualifiedName());
        if (Objects.nonNull(notNullForMapAnn)){
            PsiAnnotationMemberValue params = notNullForMapAnn.findDeclaredAttributeValue("params");
            PsiElement[] children = params.getChildren();
            Map<String,Object> map = new HashMap<>();
            Arrays.stream(children)
                    .filter(child -> child instanceof PsiAnnotation)
                    .map(child -> (PsiAnnotation)child)
                    .forEach(child -> {
                        //处理主参数
                        Parameter param = this.getParamCheckParam(child);

                        //处理子参数
                        Map<String, Object> subMap = this.buildParamCheckSub(child);
                        //判断集合类型
                        PsiAnnotationMemberValue typeAttr = child.findDeclaredAttributeValue("type");
                        String type = "";
                        if (Objects.nonNull(typeAttr) && typeAttr instanceof PsiClassObjectAccessExpression){
                            PsiClassObjectAccessExpression typeObj = (PsiClassObjectAccessExpression) typeAttr;
                            type = typeObj.getOperand().getType().getCanonicalText();
                        }
                        if (type.contains("List")){
                            //集合
                            map.put(param.getParamName(),Collections.singleton(subMap));
                        }else if (type.contains("Map")){
                            //对象
                            map.put(param.getParamName(),subMap);
                        }else {
                            map.put(param.getParamName(),
                                    PsiClassHelper
                                            .getJavaBaseTypeDefaultValue(param.getParamType(),
                                                    param.getDefaultValue()));
                        }
                    });
            return PsiClassHelper.create(psiMethod.getContainingClass()).withModule(myModule).convertMap2Json(map);
        }

        String queryJson = PsiClassHelper.create(psiMethod.getContainingClass()).withModule(myModule).convertClassToJSON(className, project);
        return queryJson;
    }

    public Map<String,Object> buildParamCheckSub(PsiAnnotation children){
        Map<String,Object> result = new HashMap<>();
        PsiAnnotationMemberValue subParamsAttr = children.findDeclaredAttributeValue("subParams");
        if (Objects.isNull(subParamsAttr)){
            return result;
        }
        Arrays.stream(subParamsAttr.getChildren())
                .filter(child -> child instanceof PsiAnnotation)
                .map(child -> (PsiAnnotation)child)
                .map(this::getParamCheckParam)
                .forEach(parameter ->
                    result.put(parameter.getParamName(),
                            PsiClassHelper
                                    .getJavaBaseTypeDefaultValue(parameter.getParamType(),
                                            parameter.getDefaultValue()))
                    );
        return result;
    }

    public Parameter getParamCheckParam(PsiAnnotation paramCheckAnn){
        Parameter parameter = new Parameter();
        PsiAnnotationMemberValue valueAttr = paramCheckAnn.findDeclaredAttributeValue("value");
        if (Objects.nonNull(valueAttr) && valueAttr instanceof PsiLiteralExpression){
            Object value = ((PsiLiteralExpression) valueAttr).getValue();
            parameter.setParamName(String.valueOf(value));
        }
        PsiAnnotationMemberValue typeAttr = paramCheckAnn.findDeclaredAttributeValue("type");
        String type = "String";
        if (Objects.nonNull(typeAttr) && typeAttr instanceof PsiClassObjectAccessExpression){
            type = typeAttr.toString().split(":")[1];
            type = type.split("\\.")[0];
        }
        parameter.setDefaultValue(getDefaultValue(paramCheckAnn));
        parameter.setRequired(isRequired(paramCheckAnn));
        parameter.setPattern(getPattern(paramCheckAnn));
        parameter.setParamType(type);
        //处理日期
        if (parameter.getParamType().toLowerCase().contains("date")){
            parameter.setDefaultValue(parameter.getPattern());
        }
        return parameter;
    }

    public String buildRequestBodyJson() {
        List<Parameter> parameterList = this.getParameterList();
        for (Parameter parameter : parameterList) {
            if (parameter.isRequestBodyFound()) {
                return buildRequestBodyJson(parameter);
            }
        }
        return null;
    }

    @NotNull
    public String buildServiceUriPath() {
        String ctrlPath = null;
        String methodPath = null;

        //判断rest服务提供方式 spring or jaxrs
        PsiClass containingClass = psiMethod.getContainingClass();
        RestSupportedAnnotationHelper annotationHelper;
        if (isSpringRestSupported(containingClass)) {
            ctrlPath = RequestMappingAnnotationHelper.getOneRequestMappingPath(containingClass);
            methodPath = RequestMappingAnnotationHelper.getOneRequestMappingPath(psiMethod);
        }else if(isJaxrsRestSupported(containingClass)){
            ctrlPath = JaxrsAnnotationHelper.getClassUriPath(containingClass);
            methodPath = JaxrsAnnotationHelper.getMethodUriPath(psiMethod);
        }

        if (ctrlPath == null) {
            return null;
        }

        if (!ctrlPath.startsWith("/")) ctrlPath = "/".concat(ctrlPath);
        if (!ctrlPath.endsWith("/")) ctrlPath = ctrlPath.concat("/");
        if (methodPath.startsWith("/")) methodPath = methodPath.substring(1, methodPath.length());

        return ctrlPath + methodPath;
    }

    @NotNull
    public String buildServiceUriPathWithParams() {
        String serviceUriPath = buildServiceUriPath();

        String params = PsiMethodHelper.create(psiMethod).buildParamString();
        // RequestMapping 注解设置了 param
        if (!params.isEmpty()) {
            StringBuilder urlBuilder = new StringBuilder(serviceUriPath);
            return urlBuilder.append(serviceUriPath.contains("?") ? "&": "?").append(params).toString();
        }
        return  serviceUriPath;
    }

    //包含 "RestController" "Controller"
    public static boolean isSpringRestSupported(PsiClass containingClass) {
        PsiModifierList modifierList = containingClass.getModifierList();

        /*return modifierList.findAnnotation(SpringControllerAnnotation.REST_CONTROLLER.getQualifiedName()) != null ||
                modifierList.findAnnotation(SpringControllerAnnotation.CONTROLLER.getQualifiedName()) != null ;*/

        return modifierList.findAnnotation(SpringControllerAnnotation.REST_CONTROLLER.getQualifiedName()) != null ||
                modifierList.findAnnotation(SpringControllerAnnotation.CONTROLLER.getQualifiedName()) != null ;
    }

    //包含 "RestController" "Controller"
    public static boolean isJaxrsRestSupported(PsiClass containingClass) {
        PsiModifierList modifierList = containingClass.getModifierList();

        return modifierList.findAnnotation(JaxrsRequestAnnotation.PATH.getQualifiedName()) != null ;
    }



    /* 生成完整 URL , 附带参数 */
    @NotNull
    public String buildFullUrlWithParams() {

        String fullUrl = buildFullUrl();

        String params = buildParamString();

        // RequestMapping 注解设置了 param
        if (!params.isEmpty()) {
            StringBuilder urlBuilder = new StringBuilder(fullUrl);
            return urlBuilder.append(fullUrl.contains("?") ? "&": "?").append(params).toString();
        }
        return  fullUrl;
    }

    @NotNull
    public String buildFullUrl() {

        String hostUri = myModule != null ? ModuleHelper.create(myModule).getServiceHostPrefix() : ModuleHelper.DEFAULT_URI;

        String servicePath = buildServiceUriPath();

        return hostUri.concat(servicePath);
    }

}
